#include "sGui.h"
#include "sComponentInstance.h"
#include "sComponentRectInstance.h"
#include "sControllerEvents.h"
#include "sActionHandler.h"

namespace sgui
{
    
CONTAINER_WRAPPER(sLibrary)& sGUI::getLibrarys()
{
    return m_librarys;
}

ID sGUI::registerGuiObject(sGUIObject *obj)
{
	ID result;
	do
	{
		result = 
			(rand() % 0xFF) +
			((rand() % 0xFF) << 8) +
			((rand() % 0xFF) << 16) +
			((rand() % 0xFF) << 24);

	} while (m_objects.find(result) != m_objects.end());

	m_objects[result] = obj;
	return result;
}
	
void sGUI::refreshGuiObject(ID oldId, ID newId)
{
	sGUIObject* obj = getGuiObject(oldId);
	if (obj)
	{
		unregisterGuiObject(oldId);
		m_objects[newId] = obj;
	}
}

void sGUI::unregisterGuiObject(ID id)
{
	std::map<ID, sGUIObject*>::iterator it = m_objects.find(id);
	if (it != m_objects.end())
	{
		m_objects.erase(it);
	}
}
	
sGUIObject*	sGUI::getGuiObject(ID id) const
{
	std::map<ID, sGUIObject*>::const_iterator it = m_objects.find(id);
	if (it != m_objects.end())
	{
		return it->second;
	}
	return NULL;
}

std::deque<inst::sComponentInstance*>& sGUI::getInstances()
{
	return m_instances;
}

const std::deque<inst::sComponentInstance*>& sGUI::getInstances() const
{
	return m_instances;
}

#pragma warning (push)
#pragma warning (disable : 4355)
sGUI::sGUI(engine::sEngine* engine) :
    m_engine(engine), m_librarys(*this), m_activeRect(NULL)
{
	m_engine->init();
	m_engine->registerObserver(this);
}
#pragma warning (pop)

sGUI::~sGUI()
{
	m_engine->unregisterObserver(this);
}
    
void sGUI::updataActiveRect(const obs::sMouseEvent &event)
{
    inst::sComponentRectInstance* rect = getRectAt(event.m_position);
	if (rect != m_activeRect)
	{
		if (m_activeRect)
		{
			m_activeRect->OnHoverRelease();
		}
		m_activeRect = rect;
		if (m_activeRect)
		{
			m_activeRect->OnHoverGet();
		}
	}
}

bool sGUI::OnEvent(const obs::sEvent& event)
{
    if (m_handler)
    {
        if (m_handler->OnEvent(event))
        {
            return true;
        }
        if (m_handler && m_handler->isEventsLocked()) return false;
    }

	for (std::deque<inst::sComponentInstance*>::const_reverse_iterator it = m_instances.rbegin(); it != m_instances.rend(); it++)
	{
		if ((*it)->OnEvent(event))
		{
			return true;
		}
	}

	switch (event.getID())
	{
		case obs::EVID_MOUSE_MOVE:
		{
			CAST_EVENT(ev_m, sMouseMoveEvent);
            updataActiveRect(ev_m);	

			return true;
		}
	}

	return false;
}
	
void sGUI::draw()
{
	sgui::engine::sDrawStack stack(*this, getEngine());
	getEngine().beginRender(stack);

	for (std::deque<inst::sComponentInstance*>::const_iterator it = m_instances.begin(); it != m_instances.end(); it++)
	{
		(*it)->draw(stack);
	}

    if (m_handler)
    {
		m_handler->draw(stack);
	}

    getEngine().endRender(stack);
}
	
inst::sComponentRectInstance* sGUI::getRectAt(const math::sPointf& point)
{
	for (std::deque<inst::sComponentInstance*>::reverse_iterator it = m_instances.rbegin(); it != m_instances.rend(); it++)
	{
		inst::sComponentRectInstance* rect = (*it)->getRectAt(point);
		if (rect)
		{
			return rect;
		}
	}
	return NULL;
}
    
void sGUI::setHandler(inst::sActionHandler *handler, const obs::sMouseEvent *event)
{
    if (m_handler)
    {
        delete m_handler;
    }
    m_handler = handler;

    if (!m_handler)
        m_activeRect = NULL;
    if (event)
        updataActiveRect(*event);
}
	
void sGUI::update(double dt)
{
	m_engine->update(dt);

    if (m_handler)
    {
        if (m_handler->update(dt))
        {
            setHandler(NULL);
        }
        else
        {
            if (m_handler->isUpdateLocked())
                return;
        }
    }

	for (std::deque<inst::sComponentInstance*>::iterator it = m_instances.begin(); it != m_instances.end(); it++)
	{
		(*it)->update(dt);
	}
}
	
inst::sComponentInstance* sGUI::addInstance(inst::sComponentInstance* instance)
{
	getInstances().push_back(instance);
	instance->updateOffset();

	return instance;
}
    
engine::sEngine& sGUI::getEngine()
{
    return *m_engine;
}

const engine::sEngine& sGUI::getEngine() const
{
    return *m_engine;
}

};